#include "ogl/model/Scene.h"

using namespace Ogl::Model;

Scene::Scene(const std::string &path)
    : m_Path(path), m_Name(fs::path(path).filename().string())
{
    Create(path);
};

bool Ogl::Model::Scene::Create(const std::string &path)
{
    static Assimp::Importer importer;

    importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT);

    const aiScene *scene = importer.ReadFile(
        path,
        aiProcess_GenSmoothNormals |
        aiProcess_FlipUVs |
        aiProcess_CalcTangentSpace |
        aiProcess_GenBoundingBoxes |
        aiProcess_Triangulate |
        aiProcess_ImproveCacheLocality |
        aiProcess_SortByPType
            // | aiProcess_PopulateArmatureData 
    );

    if (scene == nullptr || scene->mRootNode == nullptr)
    {
        wtclog::info("cannot load model %s", path.c_str());
        return false;
    }
    bool res = Create(scene, path);
    importer.FreeScene();

    return res;
}

bool Ogl::Model::Scene::Create(const aiScene *scene, const std::string &path)
{
    {

        Ogl::Gut::Image desc(800, 600, 4);
        m_MeshesRenderTarget = std::make_shared<Ogl::Gut::RenderTarget>(desc ,1);
    }

    m_RootNode = std::make_shared<Node>(scene->mRootNode);

    for (int i = 0; i < scene->mNumMeshes; i++)
    {

        auto newMesh = std::make_shared<Mesh>(scene->mMeshes[i]);
        // std::cout << "get char load mesh " << newMesh->m_Name << std::endl;

        newMesh->CreateSnapshot(this->m_RootNode.get());
        CalAABB(newMesh->m_AABB);

        m_Meshes.push_back(newMesh);
    };

    if (!CreateSnapshot())
    {
        // std::cout << "Failed create scene snap Shot" << std::endl;
    }



    for (int i = 0; i < scene->mNumAnimations; i++)
    {
        m_Animations.push_back(MakeRef<Ogl::Model::Animation>(scene->mAnimations[i]));
    }
    for (int i = 0; i < scene->mNumCameras; i++)
    {
        m_Cameras.push_back(MakeRef<Ogl::Model::Camera>(scene->mCameras[i]));
    }
    for (int i = 0; i < scene->mNumLights; i++)
    {
        m_Lights.push_back(MakeRef<Ogl::Model::Light>(scene->mLights[i]));
    }

    std::string parentPath = fs::path(path).parent_path().string();

    for (int i = 0; i < scene->mNumMaterials; i++)
    {
        m_Materials.push_back(MakeRef<Ogl::Model::Material>(scene->mMaterials[i], parentPath));
    }

    return true;
}

bool Ogl::Model::Scene::CreateSnapshot()
{
    Ref<Ogl::Phong::Effect> effect = MakeRef<Ogl::Phong::Effect>();
    effect->m_RasterizerDesc.cullFace = false;

    Ogl::Phong::LightEffect lightEffect;
    lightEffect.numDirlights = 1;

    Ogl::Phong::Material material;

    int width = 800;
    int height = 600;
    float aspect = (float)width / (float)height;
    Ogl::Math::Camera camera;
    Ogl::Math::Transform transform;


    float distanceAxis = 3.0f;

    float halfExtendsLength = glm::length(m_AABB.m_HalfExtends);

    camera.SetPosition(m_AABB.m_Center + halfExtendsLength * distanceAxis * glm::vec3(0.f, 0.0f, 1.f));
    camera.LookAt(m_AABB.m_Center);



    m_MeshesRenderTarget->Begin();

    glClearColor(.2f, 0.2f, 0.2f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    effect->Begin();
    effect->SetCamera(camera);
    effect->SetLightEffect(lightEffect);

    for (const auto& mesh : m_Meshes)
    {
        effect->RenderMesh(mesh->m_Mesh.get(), transform, material);

    }
    effect->End();

    m_MeshesRenderTarget->End();

    return true;
}

void Ogl::Model::Scene::CalAABB(const Ogl::Model::AABB& other)
{
    auto& sceneMin = this->m_AABB.m_Min;
    auto& sceneMax = this->m_AABB.m_Max;

    const auto& meshMin = other.m_Min;
    const auto& meshMax = other.m_Max;

    // ����min��max�԰�����ǰ����
        // ���³���������AABB
    sceneMin.x = std::min(sceneMin.x, meshMin.x);
    sceneMin.y = std::min(sceneMin.y, meshMin.y);
    sceneMin.z = std::min(sceneMin.z, meshMin.z);

    sceneMax.x = std::max(sceneMax.x, meshMax.x);
    sceneMax.y = std::max(sceneMax.y, meshMax.y);
    sceneMax.z = std::max(sceneMax.z, meshMax.z);

    m_AABB.CalCenterHalfExt();
}
